package vision;

import java.util.logging.Level;
import java.util.logging.Logger;
import vision.input.VideoInput;
import vision.input.JMFVideoInput;
import vision.input.VideoInputs;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.imageio.ImageIO;
import javax.swing.ComboBoxModel;
import javax.swing.JFileChooser;

/**
 *
 * @author  Kotuc
 */
public class VisionFrame extends javax.swing.JFrame {

    private static final long serialVersionUID = 134L;
    private static final String DEFAULT_IMAGE_KEY = "imageframe.default.image";
//    private static final String DEFAULT_IMAGE_VIDEO = "video";
    private static final String DEFAULT_OPERATION_KEY = "imageframe.default.operation";

    private VisionFrame() {

        initComponents();

//        jFileChooser1.setSelectedFile(new File(Setup.getRoot() + "*.jpg"));

        String val = Setup.get(DEFAULT_OPERATION_KEY);
        if (val != null) {
            setDefaultOperation(val);
        }

        ((ImagePanel) destPanel).setAutoresize(false);

    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        jFileChooser1 = new javax.swing.JFileChooser();
        destPanel = new vision.ImagePanel();
        jPanel1 = new javax.swing.JPanel();
        jButton2 = new javax.swing.JButton();
        jButton1 = new javax.swing.JButton();
        dest2sourceBtn = new javax.swing.JButton();
        jComboBox1 = new javax.swing.JComboBox();
        jToggleButton1 = new javax.swing.JToggleButton();
        processTime = new javax.swing.JLabel();
        sourcePanel = new vision.ImagePanel();
        jMenuBar1 = new javax.swing.JMenuBar();
        jMenu1 = new javax.swing.JMenu();
        loadImgMI = new javax.swing.JMenuItem();
        jSeparator3 = new javax.swing.JSeparator();
        jMenuItem1 = new javax.swing.JMenuItem();
        snapMI = new javax.swing.JMenuItem();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        setTitle("Vision Testing Interface");
        addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                formMouseClicked(evt);
            }
        });
        addWindowListener(new java.awt.event.WindowAdapter() {
            public void windowClosed(java.awt.event.WindowEvent evt) {
                formWindowClosed(evt);
            }
            public void windowClosing(java.awt.event.WindowEvent evt) {
                formWindowClosing(evt);
            }
        });
        addFocusListener(new java.awt.event.FocusAdapter() {
            public void focusGained(java.awt.event.FocusEvent evt) {
                formFocusGained(evt);
            }
        });

        destPanel.setPreferredSize(new java.awt.Dimension(320, 240));
        destPanel.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                destPanelMouseClicked(evt);
            }
        });
        destPanel.setLayout(null);
        getContentPane().add(destPanel, java.awt.BorderLayout.CENTER);

        jButton2.setText("Prev");
        jButton2.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButton2ActionPerformed(evt);
            }
        });
        jPanel1.add(jButton2);

        jButton1.setText("Next");
        jButton1.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButton1ActionPerformed(evt);
            }
        });
        jPanel1.add(jButton1);

        dest2sourceBtn.setText("<<");
        dest2sourceBtn.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                dest2sourceBtnActionPerformed(evt);
            }
        });
        jPanel1.add(dest2sourceBtn);

        jComboBox1.setEditable(true);
        jComboBox1.setModel(getComboBoxModel());
        jComboBox1.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jComboBox1ActionPerformed(evt);
            }
        });
        jPanel1.add(jComboBox1);

        jToggleButton1.setText("loop");
        jToggleButton1.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jToggleButton1ActionPerformed(evt);
            }
        });
        jPanel1.add(jToggleButton1);

        processTime.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
        processTime.setText("0");
        jPanel1.add(processTime);

        getContentPane().add(jPanel1, java.awt.BorderLayout.PAGE_START);

        sourcePanel.setPreferredSize(new java.awt.Dimension(240, 180));
        sourcePanel.setLayout(null);
        getContentPane().add(sourcePanel, java.awt.BorderLayout.WEST);

        toFront();

        jMenu1.setText("File");

        loadImgMI.setText("open ...");
        loadImgMI.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                loadImgMIActionPerformed(evt);
            }
        });
        jMenu1.add(loadImgMI);
        jMenu1.add(jSeparator3);

        jMenuItem1.setText("save ...");
        jMenuItem1.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jMenuItem1ActionPerformed(evt);
            }
        });
        jMenu1.add(jMenuItem1);

        snapMI.setText("snap");
        snapMI.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                snapMIActionPerformed(evt);
            }
        });
        jMenu1.add(snapMI);

        jMenuBar1.add(jMenu1);

        setJMenuBar(jMenuBar1);

        pack();
    }// </editor-fold>//GEN-END:initComponents

    private void formWindowClosing(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowClosing
        if (vi != null) {
            if (vi instanceof JMFVideoInput) {
                ((JMFVideoInput) vi).close();
            }
        }
        Setup.store();
    }//GEN-LAST:event_formWindowClosing

    private void dest2sourceBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_dest2sourceBtnActionPerformed
        setSourceImage(BasicOps.copy(getDestImage()));
    }//GEN-LAST:event_dest2sourceBtnActionPerformed

    private void snapMIActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_snapMIActionPerformed
        benchStart();
        setSourceImage(snap());
        //        this.setTitle("Vision Testing Interface : webcam : [" + image.getWidth() + "x" + image.getHeight() + "]");
        benchEnd();
    }//GEN-LAST:event_snapMIActionPerformed

    private void destPanelMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_destPanelMouseClicked
    }//GEN-LAST:event_destPanelMouseClicked

    private void formMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_formMouseClicked
    }//GEN-LAST:event_formMouseClicked

    private void formWindowClosed(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowClosed
    }//GEN-LAST:event_formWindowClosed

    private void loadImgMIActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_loadImgMIActionPerformed
        openImage();
        doDefaultOperation();
    }//GEN-LAST:event_loadImgMIActionPerformed
    private boolean videoLoopOn = false;

    private void jToggleButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jToggleButton1ActionPerformed
        if (videoLoopOn) {
            videoLoopOn = false;
        } else {
            videoLoopOn = true;
            videoLoop();
        }
    }//GEN-LAST:event_jToggleButton1ActionPerformed

    private void jComboBox1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jComboBox1ActionPerformed
        setDefaultOperation((String) jComboBox1.getSelectedItem());
        doDefaultOperation();
    }//GEN-LAST:event_jComboBox1ActionPerformed

    private void jMenuItem1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jMenuItem1ActionPerformed
        // save button
        if (jFileChooser1.showSaveDialog(this) == JFileChooser.APPROVE_OPTION) {

            File file = jFileChooser1.getSelectedFile();
            try {
                ImageIO.write(getDestImage(), "PNG", file);
            } catch (IOException ex) {
                Logger.getLogger(VisionFrame.class.getName()).log(Level.SEVERE, null, ex);
            }

        }
    }//GEN-LAST:event_jMenuItem1ActionPerformed

    private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed
        folderIndex++;
        setSourceImage(openImageFile(folderImages[folderIndex%folderImages.length]));
        doDefaultOperation();
    }//GEN-LAST:event_jButton1ActionPerformed

    private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton2ActionPerformed
        folderIndex--;
        setSourceImage(openImageFile(folderImages[folderIndex%folderImages.length]));
        doDefaultOperation();
    }//GEN-LAST:event_jButton2ActionPerformed

    private void formFocusGained(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_formFocusGained
        doDefaultOperation();
    }//GEN-LAST:event_formFocusGained



    private int folderIndex;
    private void loadDefaultImage() {
        String name = Setup.get(DEFAULT_IMAGE_KEY);

        if (name != null) {
            File file = new File(name);
            jFileChooser1.setSelectedFile(file);
            setSourceImage(openImageFile(file));
//
            try {
                folderImages = file.getParentFile().listFiles(new FilenameFilter() {
                    public boolean accept(File dir, String name) {
                        return name.endsWith("png")||name.endsWith("jpg")||name.endsWith("PNG")||name.endsWith("JPG");
                    }
                });
                this.folderIndex = Arrays.binarySearch(folderImages, file);
            } catch (Exception e) {
                e.printStackTrace();
            }

//            if (DEFAULT_IMAGE_VIDEO.equals(name)) {
//                setSourceImage(snap());
//            } else {
//                setSourceImage(openImage(new File(name)));
//            }
        }
    }

    /**
     * Creates list of methods in Tests class
     * @return
     */
    private ComboBoxModel getComboBoxModel() {
        List<String> methods = new ArrayList<String>();
        Class ops = Tests.class;
        for (Method method : ops.getDeclaredMethods()) {

            String methodName = ops.getSimpleName() + "." + method.getName();

            Class[] params = method.getParameterTypes();

            // check method headline so can be invoked automaticaly
            boolean suitable =
                    (params.length == 1) &&
                    (BufferedImage.class.equals(params[0]));
            if (suitable) {
                methods.add(methodName);
//                System.out.println(methodName + " OK " + method);
            } else {
//                System.err.println(methodName + " INVALID " + method);
            }
        }
        return new javax.swing.DefaultComboBoxModel(methods.toArray(new String[0]));
    }
    private String defaultOperation;

    private void setDefaultOperation(String defaultOperation) {
        if (defaultOperation == null) {
            defaultOperation = "";
        }
        this.defaultOperation = defaultOperation;
        jComboBox1.setSelectedItem(defaultOperation);
        Setup.set(DEFAULT_OPERATION_KEY, defaultOperation);
    }

    private void doDefaultOperation() {
        doOperation(defaultOperation);
    }

    private void videoLoop() {
        new Thread() {

            @Override
            public void run() {
                while (videoLoopOn) {
//                    System.out.println("snap:");
                    setSourceImage(snap());

//                            try {
//                                Thread.sleep(200);
//                            } catch (InterruptedException ex) {
//                                ex.printStackTrace();
//                            }
//                    System.out.println("operation:");
                    doDefaultOperation();
//                            try {
//                                Thread.sleep(200);
//                            } catch (InterruptedException ex) {
//                                ex.printStackTrace();
//                            }
                }
            }
        }.start();

    }

    private void doOperation(String opName) {
        try {
            String[] names = opName.split("\\.");
            System.out.println("operation: " + opName + " = " + Arrays.toString(names) + " " + getSourceImage().getWidth() + "x" + getSourceImage().getHeight() + "px");
            Method method = Class.forName("vision." + names[0]).getDeclaredMethod(names[1], BufferedImage.class);
            benchStart();
            setDestImage((BufferedImage) method.invoke(null, getSourceImage()));
            benchEnd();
        } catch (Exception ex) {
//            System.err.println("ImageFrame.doOperation:" + ex.getMessage());
            ex.printStackTrace();
        }
    }

    /**
     * loads BufferedImage from specified file
     */
    private void openImage() {

        setSourceImage(selectImage());

        //        this.setTitle("Vision Testing Interface : "+file.getName()+" : ["+img.getWidth()+"x"+img.getHeight()+"]");

    }
    private File[] folderImages;

    /**
     * 
     * @param imgfile
     * @return
     */
    private static BufferedImage openImageFile(File imgfile) {

        BufferedImage img = null;

        //        File file = jFileChooser1.getSelectedFile();

        System.out.println("loading image " + imgfile.getAbsolutePath());

        try {

            img = ImageIO.read(imgfile);

            Setup.set(DEFAULT_IMAGE_KEY, imgfile.getAbsolutePath());

            return img;
        } catch (IOException ex) {
            ex.printStackTrace();
            return null;
        }


    }

    private BufferedImage selectImage() {
        if (jFileChooser1.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {

            File file = jFileChooser1.getSelectedFile();

            return openImageFile(file);

        }
        return null;
    }
    private BufferedImage destImage = null;

    private void setDestImage(BufferedImage image) {
        destImage = image;
        ((ImagePanel) destPanel).setImage(image);
        //        repaint();
    }
    private BufferedImage sourceImage = null;

    private BufferedImage getDestImage() {
        return destImage;
    }

    private void setSourceImage(BufferedImage image) {
        if (image.getWidth() > 640) {
            image = BasicOps.getResizedImage(image, 640, 480);
//        image = BasicOps.getResizedImage(image, 320, 240);
//        image = BasicOps.getResizedImage(image, 160, 120);

        }
        this.sourceImage = image;
        ((ImagePanel) sourcePanel).setImage(image);
        this.setTitle(image.getWidth() + "x" + image.getHeight());
        //        repaint();
    }
    private VideoInput vi;

    private BufferedImage getSourceImage() {
        return BasicOps.copy(sourceImage);
    }

    private void openVideoInput() {
        vi = VideoInputs.getVideo();
    }

    private BufferedImage snap() {
        if (vi == null) {
            openVideoInput();
        }
        return vi.snap();
    }
    private long startTime = 0;

    private void benchStart() {
        startTime = System.currentTimeMillis();
    }

    private void benchEnd() {
        processTime.setText("" + (System.currentTimeMillis() - startTime) + "ms");
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {

            public void run() {
                System.out.println("creating ImageFrame");
                VisionFrame iframe = new VisionFrame();
                iframe.setVisible(true);
                System.out.println("loading image");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException ex) {
                    Logger.getLogger(VisionFrame.class.getName()).log(Level.SEVERE, null, ex);
                }
                iframe.loadDefaultImage();
                iframe.doDefaultOperation();
            }
        });

    }
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton dest2sourceBtn;
    private javax.swing.JPanel destPanel;
    private javax.swing.JButton jButton1;
    private javax.swing.JButton jButton2;
    private javax.swing.JComboBox jComboBox1;
    static javax.swing.JFileChooser jFileChooser1;
    private javax.swing.JMenu jMenu1;
    private javax.swing.JMenuBar jMenuBar1;
    private javax.swing.JMenuItem jMenuItem1;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JSeparator jSeparator3;
    private javax.swing.JToggleButton jToggleButton1;
    private javax.swing.JMenuItem loadImgMI;
    private javax.swing.JLabel processTime;
    private javax.swing.JMenuItem snapMI;
    private javax.swing.JPanel sourcePanel;
    // End of variables declaration//GEN-END:variables
}
